package com.imooc.bilibili.websocket;

import com.alibaba.fastjson.JSONObject;
import com.imooc.bilibili.domain.Danmu;
import com.imooc.bilibili.domain.dto.DanMuDto;
import com.imooc.bilibili.rabbit.RabbitSendMessage;
import com.imooc.bilibili.service.DanMuService;
import com.imooc.bilibili.util.TokenUtil;
import org.apache.ibatis.annotations.Param;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * ProjectName imooc-bilibili
 *
 * @author xieyucan
 * <br>CreateDate 2022/10/26 11:12
 */
@Component
@ServerEndpoint("/imserver/{token}")
public class WebSocketService {

    public Session getSession() {
        return session;
    }

    public String getSessionId() {
        return sessionId;
    }

    private static final Logger LOGGER= (Logger) LoggerFactory.getLogger(WebSocketService.class);

    private Session session;

    private String sessionId;

    public static final ConcurrentHashMap<String,WebSocketService> WEBSOCKET_MAP=new ConcurrentHashMap<>();

    private Long userId;

    private static final AtomicInteger ONLINE_COUNT=new AtomicInteger(0);

    private static ApplicationContext APPLICATION_CONTEXT;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        APPLICATION_CONTEXT = applicationContext;
    }

    @OnOpen
    public void openConnection(Session session, @Param("token")String token)
    {
        try {
            this.userId=TokenUtil.verifyToken(token);
        }catch (Exception e){

        }
        this.session=session;
        this.sessionId=session.getId();
        if(WEBSOCKET_MAP.containsKey(sessionId)) {
            WEBSOCKET_MAP.remove(sessionId);
            WEBSOCKET_MAP.put(sessionId,this);
        } else {
            WEBSOCKET_MAP.put(sessionId,this);
            ONLINE_COUNT.getAndIncrement();
        }
        LOGGER.info("用户连接成功："+sessionId+".当前在线人数为:"+ONLINE_COUNT.get());
        try {
            this.sendMessage("0");
        }catch (Exception e){
            LOGGER.error("连接异常");
        }
    }

    /**
     * 发送消息
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    @OnClose
    public void closeConnection()
    {
        if(WEBSOCKET_MAP.containsKey(this.sessionId))
        {
            WEBSOCKET_MAP.remove(sessionId);
            ONLINE_COUNT.decrementAndGet();
        }
        LOGGER.info("用户退出："+sessionId+".当前在线人数为:"+ONLINE_COUNT.get());
    }

    @OnMessage
    public void onMessage(String message)
    {
        LOGGER.info("用户："+sessionId+"发送消息"+message);
        //将消息群发送出去
        try {
            //群发消息
            for(Map.Entry<String,WebSocketService> entry:WEBSOCKET_MAP.entrySet())
            {
                WebSocketService webSocketService = entry.getValue();
                RabbitSendMessage rabbitSendMessage=(RabbitSendMessage) APPLICATION_CONTEXT.getBean("rabbitSendMessage");
                DanMuDto danMuDto = new DanMuDto();
                danMuDto.setMessage(message);
                danMuDto.setSessionId(webSocketService.getSessionId());
                Message sendMessage = new Message(JSONObject.toJSONString(danMuDto).getBytes(StandardCharsets.UTF_8));
                rabbitSendMessage.sendDanMuMessage(sendMessage);
            }
            //将消息存储起来
            if(userId!=null)
            {
                Danmu danmu = JSONObject.parseObject(message, Danmu.class);
                danmu.setCreateTime(new Date());
                danmu.setUserId(userId);
                DanMuService danMuService=(DanMuService)APPLICATION_CONTEXT.getBean("danMuService");
                danMuService.asyncAddDanMu(danmu);
                danMuService.addDanMuToRedis(danmu);
            }
        }catch (Exception e){
            LOGGER.error("弹幕接收出现问题");
            e.printStackTrace();
        }

    }

    @Scheduled(fixedRate = 5000)
    private void noticeOnlineCount() throws IOException {
        for(Map.Entry<String,WebSocketService> entry:WEBSOCKET_MAP.entrySet())
        {
            WebSocketService webSocketService = entry.getValue();
            if(webSocketService.session.isOpen())
            {
                JSONObject jsonObject=new JSONObject();
                jsonObject.put("onLineCount",ONLINE_COUNT.get());
                jsonObject.put("msg","当前在线人数为"+ONLINE_COUNT.get());
                webSocketService.sendMessage(jsonObject.toJSONString());
            }
        }
    }
}
